CREATE FUNCTION controllaUnivocitaBiglietto() RETURNS TRIGGER AS $$
BEGIN
	-- cerco se c'è un altro biglietto valido con la stessa poltrona
	PERFORM NumBiglietto
		FROM Biglietto AS B
		WHERE B.IDPoltrona = NEW.IDPoltrona AND B.Valido = TRUE
			AND B.CodiceSpettacolo = NEW.CodiceSpettacolo
			AND B.NumBiglietto <> NEW.NumBiglietto; --permetto le modifiche al singolo biglietto
	IF FOUND THEN
		RAISE EXCEPTION 'Seduta occupata per quello spettacolo!!';
	END IF;

	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER "bigliettoUnivoco" BEFORE INSERT OR UPDATE 
	ON Biglietto
	FOR EACH ROW
	EXECUTE PROCEDURE controllaUnivocitaBiglietto();

CREATE FUNCTION controllaMassimoNumeroPrenotazioni() RETURNS TRIGGER AS $$
DECLARE
	numeroBigliettiPrenotati INT;
BEGIN
	--non opero il controllo se la nuova riga è di un biglietto non valido (utile per eliminare i biglietti inseriti)
	IF NEW.Valido = FALSE THEN
		RETURN NEW;
	END IF;

	--cerco quanti biglietti 
	SELECT Count(NumBiglietto)::INT INTO numeroBigliettiPrenotati
		FROM Biglietto AS B
		WHERE B.Pagato = FALSE AND B.Valido = TRUE 
			AND B.CodiceSpettacolo = NEW.CodiceSpettacolo
			AND B.IDUtente = NEW.IDUtente;

	IF numeroBigliettiPrenotati >= 5 THEN
		RAISE EXCEPTION 'Ecceduto numero di biglietti prenotati e non pagati!';
	END IF;

	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER "massimePrenotazioni" BEFORE INSERT OR UPDATE
	ON Biglietto
	FOR EACH ROW
	EXECUTE PROCEDURE controllaMassimoNumeroPrenotazioni();



CREATE FUNCTION controllaMinimoUsoProiettori() RETURNS TRIGGER AS $$
DECLARE
	totaleOre INT;
	durataProiezione INT;
BEGIN
	-- calcolo durata uso dei proiettori	
	SELECT SUM(Durata) INTO totaleOre
		FROM R_Proiettato
		WHERE R_Proiettato.CodiceSpettacolo = NEW.CodiceSpettacolo;
	
	-- leggo durata dichiarata
	SELECT Evento.DurataProiezione INTO durataProiezione
		FROM Evento INNER JOIN Palinsesto ON Evento.Codice = Palinsesto.CodiceEvento
			INNER JOIN Spettacolo ON Spettacolo.CodicePalinsesto = Palinsesto.Codice
		WHERE Spettacolo.Codice = NEW.CodiceSpettacolo;

	IF durataProiezione > totaleOre THEN
		RAISE EXCEPTION 'Numero di minuti totali di proiezione inferiori al dichiarato nell''evento';
	END IF;
	
	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE FUNCTION controllaMinimoUsoProiettoriFromSpettacolo() RETURNS TRIGGER AS $$
DECLARE
	totaleOre INT;
	durataProiezione INT;
BEGIN
	-- non intervengo se non è cambiata la durata - minimizzo impatto su BdD
	IF (NEW.CodicePalinsesto <> OLD.CodicePalinsesto) AND (NEW.Valido = TRUE) THEN
		RAISE EXCEPTION 'Impossibile cambiare voce di palinsesto associata ad uno spettacolo!';
	END IF;

	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE FUNCTION controllaMinimoUsoProiettoriFromPalinsesto() RETURNS TRIGGER AS $$
DECLARE
	durataProiezione INT;
BEGIN
	-- non intervengo se non è cambiata la durata - minimizzo impatto su BdD
	IF NEW.CodiceEvento <> OLD.CodiceEvento  THEN
		RAISE EXCEPTION 'Impossibile cambiare evento associato ad una voce di palinsesto!';
	END IF;
	
	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;


CREATE FUNCTION controllaMinimoUsoProiettoriFromEvento() RETURNS TRIGGER AS $$
BEGIN
	-- non intervengo se non è cambiata la durata - minimizzo impatto su BdD
	IF NEW.durataProiezione = OLD.DurataProiezione THEN RETURN NEW; END IF;

	PERFORM
		spettacolo.codice, sum(r_proiettato.durata) as DurataTotale
		FROM Spettacolo INNER JOIN r_Proiettato ON Spettacolo.codice = r_proiettato.codiceSpettacolo
			INNER JOIN Palinsesto ON Palinsesto.codice = Spettacolo.codicePalinsesto
		WHERE Spettacolo.valido = TRUE AND Palinsesto.codiceEvento = NEW.Codice
		GROUP BY spettacolo.codice
		HAVING sum(r_proiettato.durata) < NEW.DurataProiezione;
			

	IF FOUND THEN
		RAISE EXCEPTION 'Numero di minuti totali di proiezione inferiori al dichiarato nell''evento';
	END IF;
	
	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;


CREATE CONSTRAINT TRIGGER "minimoUsoProiettoreR_Proiettato"
	AFTER update OR insert
	ON R_Proiettato
	DEFERRABLE INITIALLY DEFERRED 
	FOR EACH ROW
	EXECUTE PROCEDURE controllaMinimoUsoProiettori();

CREATE CONSTRAINT TRIGGER "minimoUsoProiettoreSpettacolo"
	AFTER update
	ON Spettacolo
	FOR EACH ROW
	EXECUTE PROCEDURE controllaMinimoUsoProiettoriFromSpettacolo();

CREATE CONSTRAINT TRIGGER "minimoUsoProiettorePalinsesto"
	AFTER update
	ON Palinsesto
	FOR EACH ROW
	EXECUTE PROCEDURE controllaMinimoUsoProiettoriFromPalinsesto();

CREATE CONSTRAINT TRIGGER "minimoUsoProiettoreEvento"
	AFTER update
	ON Evento
	FOR EACH ROW
	EXECUTE PROCEDURE controllaMinimoUsoProiettoriFromEvento();


CREATE FUNCTION controllaMassimoUsoDiAbbonamento() RETURNS TRIGGER AS $$
DECLARE
	ingressiUsati INT;
	totaleIngressi INT;
BEGIN
	SELECT INTO ingressiUsati, totaleIngressi
		COUNT(B.NumBiglietto), A.TotaleIngressi
			FROM Biglietto AS B INNER JOIN R_Consuma ON B.NumBiglietto = R_Consuma.NumeroBiglietto
				INNER JOIN Abbonamento AS A ON R_Consuma.NumAbbonam = A.Numero
			WHERE B.Valido = TRUE AND B.Pagato = TRUE AND A.Numero = NEW.NumAbbonam
			GROUP BY A.Numero, A.TotaleIngressi;

	IF ingressiUsati >= totaleIngressi THEN
		RAISE EXCEPTION 'Abbonamento Esaurito';
	END IF;
	
	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER "usoAbbonamenti" BEFORE INSERT OR UPDATE
	ON R_Consuma
	FOR EACH ROW
	EXECUTE PROCEDURE controllaMassimoUsoDiAbbonamento();


CREATE FUNCTION controllaSpettacoliNonInSovrapposizione() RETURNS TRIGGER AS $$
DECLARE
	oraInizioSpettacoloAttuale TIMESTAMP;
	durataSpettacoloAttuale INTERVAL;
BEGIN
	-- ottengo la data di inizio e durata dello spettacolo che sto inserendo/modificando
	SELECT INTO oraInizioSpettacoloAttuale, durataSpettacoloAttuale
		dataOra, evento.duratainsala * INTERVAL '1 minute'
			FROM spettacolo INNER JOIN palinsesto ON spettacolo.codicepalinsesto = palinsesto.codice
				INNER JOIN evento on evento.codice = palinsesto.codiceevento
			WHERE spettacolo.codice = NEW.CodiceSpettacolo;

	PERFORM Spettacolo.Codice
		FROM spettacolo INNER JOIN palinsesto ON spettacolo.codicepalinsesto = palinsesto.codice
			INNER JOIN evento on evento.codice = palinsesto.codiceevento
			INNER JOIN r_proiettato ON r_proiettato.codicespettacolo = spettacolo.codice
		WHERE r_proiettato.idSala = NEW.idSala AND	--spettacolo nella stessa sala
			Spettacolo.codice <> NEW.codiceSpettacolo AND	-- non impedisco l'aggiunta di nuovi proiettori per lo stesso spettacolo
			Spettacolo.valido = TRUE AND
			(palinsesto.dataora, (evento.duratainsala * INTERVAL '1 minute'))
				OVERLAPS (oraInizioSpettacoloAttuale, durataSpettacoloAttuale); 	-- lo spettacolo si sovrappone ad altri

	IF FOUND THEN
		RAISE EXCEPTION 'Lo spettacolo si sovrappone ad un altro definito nella stessa sala';
	END IF;
	
	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER "spettacoliNonSovrapposti" BEFORE INSERT OR UPDATE
	ON R_Proiettato
	FOR EACH ROW
	EXECUTE PROCEDURE controllaSpettacoliNonInSovrapposizione();

CREATE FUNCTION controllaEsistenzaPrestitoPerFilm() RETURNS TRIGGER AS $$
DECLARE
	durataFilm INTERVAL;
BEGIN
	-- verifico se è un film, recuperando anche la sua durata
	SELECT INTO durataFilm
		evento.durataInSala * INTERVAL '1 minute'
			FROM evento INNER JOIN film ON evento.codice = film.codice
			WHERE evento.codice = NEW.CodiceEvento;

	IF NOT FOUND THEN
		RETURN NEW;	-- non devo verificare nulla se non è un film - gestione a carico della contabilità
	END IF;

	IF NEW.in3d THEN
		PERFORM Noleggio.codicefilm, noleggio.dataInizio
			FROM Noleggio
			WHERE Noleggio.codiceFilm = NEW.CodiceEvento AND
				Noleggio.in3d = TRUE AND
				Noleggio.DataInizio <= NEW.dataora AND (New.DataOra + durataFilm) <=  Noleggio.DataFine + INTERVAL '1 day'; 
			-- devo aggiungere un giorno alla data di fine perché il noleggio si intende fino alle 24:00 dell'ultimo giorno

		IF NOT FOUND THEN
			RAISE EXCEPTION 'Non esiste un noleggio con 3D abilitato che copra questa proiezione';
		END IF;
	ELSE
		PERFORM Noleggio.codicefilm, noleggio.dataInizio
			FROM Noleggio
			WHERE Noleggio.codiceFilm = NEW.CodiceEvento AND
				Noleggio.in2d = TRUE AND
				Noleggio.DataInizio <= NEW.dataora AND (New.DataOra + durataFilm) <=  Noleggio.DataFine + INTERVAL '1 day'; 
			-- devo aggiungere un giorno alla data di fine perché il noleggio si intende fino alle 24:00 dell'ultimo giorno

		IF NOT FOUND THEN
			RAISE EXCEPTION 'Non esiste un noleggio con 2D abilitato che copra questa proiezione';
		END IF;
	END IF;

	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER "esistenzaPrestitoPerFilm" BEFORE INSERT OR UPDATE
	ON Palinsesto
	FOR EACH ROW
	EXECUTE PROCEDURE controllaEsistenzaPrestitoPerFilm();


CREATE FUNCTION controllaNonPalinsestoOltreDataFine() RETURNS TRIGGER AS $$

BEGIN
	IF NEW.CodiceFilm <> OLD.CodiceFilm THEN
		RAISE EXCEPTION 'Non si può riassegnare un noleggio ad un film diverso';
	END IF;
	IF NEW.DataInizio <> OLD.DataInizio THEN
		RAISE EXCEPTION 'Non si può modificare la data di inizio di un noleggio';
	END IF;
	-- per primo controllo quelli in 3d

	PERFORM Palinsesto.Codice
		FROM Palinsesto INNER JOIN Evento ON Evento.Codice = Palinsesto.CodiceEvento
		WHERE Palinsesto.codiceEvento = NEW.CodiceFilm AND Palinsesto.in3d = TRUE AND
			(Palinsesto.dataOra + (Evento.durataInSala * interval '1 minute'))
				> ALL (SELECT DataFine + INTERVAL '1 day' FROM Noleggio AS NoleggioSub WHERE NoleggioSub.CodiceFilm = New.CodiceFilm
						AND NoleggioSub.in3d = TRUE AND NoleggioSub.DataInizio <= Palinsesto.DataOra);
	IF FOUND THEN
		RAISE EXCEPTION 'Palinsesti 3D definiti che diverrebbero non coperti da noleggi';
	END IF;

	-- ora i 2D
	
	PERFORM Palinsesto.Codice
		FROM Palinsesto INNER JOIN Evento ON Evento.Codice = Palinsesto.CodiceEvento
		WHERE Palinsesto.codiceEvento = NEW.CodiceFilm AND Palinsesto.in3d = FALSE AND
			(Palinsesto.dataOra + (Evento.durataInSala * interval '1 minute'))
				> ALL (SELECT DataFine + INTERVAL '1 day' FROM Noleggio AS NoleggioSub WHERE NoleggioSub.CodiceFilm = New.CodiceFilm
						AND NoleggioSub.in2d = TRUE AND NoleggioSub.DataInizio <= Palinsesto.DataOra);

	IF FOUND THEN
		RAISE EXCEPTION 'Palinsesti 2D definiti che diverrebbero non coperti da noleggi';
	END IF;

	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER "nonPalinsestoOltreDataFine" AFTER UPDATE
	ON Noleggio
	FOR EACH ROW
	EXECUTE PROCEDURE controllaNonPalinsestoOltreDataFine();

CREATE FUNCTION controllaBiglietto3DSoloPerSpettacolo3D() RETURNS TRIGGER AS $$
DECLARE
	palinsesto3d BOOLEAN;
	fascia3d BOOLEAN;
BEGIN
	SELECT INTO palinsesto3d 
		palinsesto.in3d 
			FROM spettacolo INNER JOIN palinsesto ON palinsesto.codice = spettacolo.codicepalinsesto
			WHERE spettacolo.codice = NEW.codicespettacolo;
	SELECT INTO fascia3d 
		in3d FROM tariffa where tariffa.fascia = NEW.fascia;

	IF (NOT palinsesto3d) AND fascia3d THEN
		RAISE EXCEPTION 'Utilizzato biglietto 3D per spettacolo non in 3D!';
	END IF;
	
	IF palinsesto3d AND NOT fascia3d THEN
		RAISE EXCEPTION 'Utilizzato biglietto 2D per spettacolo 3D!';
	END IF;

	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER "biglietto3DSoloPerSpettacolo3D" BEFORE INSERT OR UPDATE
	ON Biglietto
	FOR EACH ROW
	EXECUTE PROCEDURE controllaBiglietto3DSoloPerSpettacolo3D();

CREATE FUNCTION controllaUsoAbbonamentoNonPerBiglietti3D() RETURNS TRIGGER AS $$
DECLARE
	fascia3d BOOLEAN;
BEGIN

	SELECT INTO fascia3d 
		tariffa.in3d FROM tariffa INNER JOIN biglietto ON biglietto.fascia = tariffa.fascia
			WHERE biglietto.numbiglietto = NEW.numerobiglietto;

	IF fascia3d THEN
		RAISE EXCEPTION 'Utilizzato abbonamento per uno spettacolo 3D!';
	END IF;
	
	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER "usoAbbonamentoNonPerBiglietti3D" BEFORE INSERT OR UPDATE
	ON r_consuma
	FOR EACH ROW
	EXECUTE PROCEDURE controllaUsoAbbonamentoNonPerBiglietti3D();

CREATE FUNCTION controllaBigliettiEmessiPerUpdateSpettacolo() RETURNS TRIGGER AS $$

BEGIN
	PERFORM Biglietto.numbiglietto
		FROM Biglietto WHERE valido = TRUE AND codiceSpettacolo = NEW.Codice;
	
	IF FOUND THEN
		RAISE EXCEPTION 'Non si possono effettuare modifiche ad uno spettacolo con biglietti validi emessi!';
	END IF;
	
	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER bigliettiEmessiPerUpdateSpettacolo BEFORE UPDATE
	ON Spettacolo
	FOR EACH ROW
	EXECUTE PROCEDURE controllaBigliettiEmessiPerUpdateSpettacolo();

CREATE FUNCTION controllaBigliettoPagatoSoloDaUtenteVerificato() RETURNS TRIGGER AS $$

BEGIN
	IF NEW.pagato = false THEN --se il biglietto non è uno di quelli pagati non serve verificare l'utente
		RETURN NEW;
	END IF;

	PERFORM utente.idutente
		FROM Utente WHERE verificato = TRUE AND attivo = TRUE AND idutente = NEW.idutente;

	IF NOT FOUND THEN
		RAISE EXCEPTION 'I biglietti possono essere acquistati solo da utenti verificati!';
	END IF;

	RETURN NEW;
END
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER bigliettoPagatoSoloDaUtenteVerificato BEFORE INSERT OR UPDATE
	ON Biglietto
	FOR EACH ROW
	EXECUTE PROCEDURE controllaBigliettoPagatoSoloDaUtenteVerificato();

